﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;

namespace Language.Intellisense.Signature
{
    internal class Signature : ISignature
    {
        #region Private Data

        private ITrackingSpan _applicableToSpan;
        private ITextView _textView;

        private string _content;
        private List<Parameter> _parameters;
        private List<ITrackingSpan> _parameterSpans;

        private Parser.Function _source;
        private Stub.Function _overload;

        private IParameter _activeParameter;

        #endregion Private Data

        #region Public Properties

        public Stub.Function Overload => _overload;

        #endregion Public Properties

        #region C-Tor

        public Signature( Parser.Function source, Stub.Function overload, ITextView textView, ITrackingSpan applicableToSpan )
        {
            _textView = textView;
            _overload = overload;
            _applicableToSpan = applicableToSpan;

            _content = BuildContent( _overload );
            _parameters = ConstructParameters( _overload, this );

            SetSource( source, textView.TextBuffer.CurrentSnapshot );

            CurrentParameterChanged?.GetType();
            
            _textView.Caret.PositionChanged += Caret_PositionChanged;

            _activeParameter = CalculateParameter( _textView.Caret.Position );
        }

        #endregion C-Tor

        #region Public Methods

        public void SetSource( Parser.Function source, ITextSnapshot snapshot )
        {
            Debug.Print( "Signature.SetSource" );

            _source = source;
            _parameterSpans = ConstructParameterSpans( _source, snapshot );

            UpdateActiveParameter( _textView.Caret.Position );
        }

        public void Dispose()
        {
            _textView.Caret.PositionChanged -= Caret_PositionChanged;
        }

        #endregion Public Methods

        #region Private Methods

        private void UpdateActiveParameter( CaretPosition position )
        {
            IParameter oldParam = _activeParameter;
            _activeParameter = CalculateParameter( position );

            CurrentParameterChanged?.Invoke( this, new CurrentParameterChangedEventArgs( oldParam, _activeParameter ) );
        }

        private IParameter CalculateParameter( CaretPosition position )
        {
            SnapshotPoint? point = position.Point.GetPoint( _textView.TextBuffer, PositionAffinity.Successor );

            int? index = CalculateParameterIndex( point );

            if ( index == null || index >= _parameters.Count )
                return null;

            return _parameters[ index.Value ];
        }

        private int? CalculateParameterIndex( SnapshotPoint? point )
        {
            if ( point != null )
            {
                for ( int i = 0; i < _source.Parameters.Count; ++i )
                {
                    Parser.Parameter parameter = _source.Parameters[ i ];

                    if ( point.Value > parameter.Start.Start && point.Value < parameter.End.End )
                    {
                        Debug.Print( $"Signature.CalculateParameterIndex: {i} Point: {point.Value.Position} Parameter:{parameter.Start.Start}-{parameter.End.End}" );
                        return i;
                    }
                }
            }

            return null;
        }

        #endregion Private Methods

        #region Event Handlers

        private void Caret_PositionChanged( object sender, CaretPositionChangedEventArgs e )
        {
            Debug.Print( $"Signature.Caret_PositionChanged O:{e.OldPosition.BufferPosition.Position} N:{e.NewPosition.BufferPosition.Position}" );

            UpdateActiveParameter( e.NewPosition );
        }

        #endregion Event Handlers

        #region Private Functions

        private static string BuildContent( Stub.Function function )
        {
            string parameters = string.Join( ", ", from param in function.Parameters select param.Name );
            return $"{function.Name}( {parameters} )";
        }

        private static List<Parameter> ConstructParameters( Stub.Function function, ISignature parent )
        {
            List<Parameter> parameters = new List<Parameter>();

            for ( int i = 0; i < function.Parameters.Count; ++i )
            {
                parameters.Add( new Parameter( parent, function, i ) );
            }

            return parameters;
        }

        private static List<ITrackingSpan> ConstructParameterSpans( Parser.Function source, ITextSnapshot snapshot )
        {
            List<ITrackingSpan> trackingSpans = new List<ITrackingSpan>();

            foreach( var parameter in source.Parameters )
            {
                Span span = new Span( parameter.Start.Start, parameter.End.End - parameter.Start.Start );

                ITrackingSpan trackingSpan = snapshot.CreateTrackingSpan( span, SpanTrackingMode.EdgeInclusive );

                trackingSpans.Add( trackingSpan );
            }

            return trackingSpans;
        }

        #endregion Private Functions

        #region ISignature

        ITrackingSpan ISignature.ApplicableToSpan => _applicableToSpan;

        string ISignature.Content => _content;

        IParameter ISignature.CurrentParameter => _activeParameter;

        string ISignature.Documentation => "Signature Documentation!";

        ReadOnlyCollection<IParameter> ISignature.Parameters => _parameters.ConvertAll( parameter => (IParameter)parameter ).AsReadOnly();

        string ISignature.PrettyPrintedContent => "Very pretty!";

        public event EventHandler<CurrentParameterChangedEventArgs> CurrentParameterChanged;

        #endregion ISignature
    }
}
